home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / hplip / fax / fax.py < prev    next >
Text File  |  2008-10-13  |  29KB  |  874 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. from __future__ import generators
  23.  
  24. # Std Lib
  25. import sys
  26. import os
  27. import threading
  28. import cPickle
  29. import time
  30. from cStringIO import StringIO
  31. import struct
  32.  
  33. # Local
  34. from base.g import *
  35. from base.codes import *
  36. from base.ldif import LDIFParser
  37. from base import device, utils, vcard
  38. from prnt import cups
  39.  
  40. try:
  41.     import coverpages
  42. except ImportError:
  43.     pass
  44.     
  45. try:
  46.     import dbus
  47. except ImportError:
  48.     log.error("dbus is required for PC send fax.")
  49.     
  50.     
  51. # Update queue values (Send thread ==> UI)
  52. STATUS_IDLE = 0
  53. STATUS_PROCESSING_FILES = 1
  54. STATUS_DIALING = 2
  55. STATUS_CONNECTING = 3
  56. STATUS_SENDING = 4
  57. STATUS_COMPLETED = 5
  58. STATUS_CREATING_COVER_PAGE = 6
  59. STATUS_ERROR = 7
  60. STATUS_BUSY = 8
  61. STATUS_CLEANUP = 9
  62.  
  63. # Event queue values (UI ==> Send thread)
  64. EVENT_FAX_SEND_CANCELED = 1
  65. # Other values in queue are:
  66. #EVENT_FAX_RENDER_COMPLETE_BEGIN = 8010
  67. #EVENT_FAX_RENDER_COMPLETE_SENDDATA = 8011
  68. #EVENT_FAX_RENDER_COMPLETE_END = 8012
  69.  
  70. # **************************************************************************** #
  71. # HPLIP G3 Fax File Format (big endian)
  72. #
  73. # #==============================================#
  74. # # File Header: Total 28 bytes                  #
  75. # #..............................................#
  76. # # Magic bytes: 8 bytes ("hplip_g3")            #
  77. # # Format version: 8 bits (1)                   #
  78. # # Total pages in file(=p): 32 bits             #
  79. # # Hort DPI: 16 bits (200 or 300)               #
  80. # # Vert DPI: 16 bits (100, 200, or 300)         #
  81. # # Page Size: 8 bits (0=Unk, 1=Letter, 2=A4,    #
  82. # #                    3=Legal)                  #
  83. # # Resolution: 8 bits (0=Unk, 1=Std, 2=Fine,    #
  84. # #                     3=300DPI)                #
  85. # # Encoding: 8 bits (2=MH, 4=MMR, 7=JPEG)       #
  86. # # Reserved1: 32 bits (0)                       #
  87. # # Reserved2: 32 bits (0)                       #
  88. # #----------------------------------------------#
  89. # # Page 1 Header: Total 24 bytes                #
  90. # #..............................................#
  91. # # Page number: 32 bits (1 based)               #
  92. # # Pixels per row: 32 bits                      #
  93. # # Rows this page: 32 bits                      #
  94. # # Image bytes this page(=x): 32 bits           #
  95. # # Thumbnail bytes this page(=y): 32 bits       #
  96. # #  (thumbnail not present if y == 0)           #
  97. # #  (encoding?)                                 #
  98. # #     letter: 134 px wide x 173 px high        #
  99. # #     legal:  134 px wide x 221 px high        #
  100. # #     a4 :    134 px wide x 190 px high        #
  101. # # Reserved3: 32 bits (0)                       #
  102. # #..............................................#
  103. # # Image data: x bytes                          #
  104. # #..............................................#
  105. # # Thumbnail data: y bytes (if present)         #
  106. # #----------------------------------------------#
  107. # # Page 2 Header: Total 24 bytes                #
  108. # #..............................................#
  109. # # Image Data                                   #
  110. # #..............................................#
  111. # # Thumbnail data (if present)                  #
  112. # #----------------------------------------------#
  113. # # ... Pages 3 - (p-1) ...                      #
  114. # #----------------------------------------------#
  115. # # Page p Header: Total 24 bytes                #
  116. # #..............................................#
  117. # # Image Data                                   #
  118. # #..............................................#
  119. # # Thumbnail data (if present)                  #
  120. # #==============================================#
  121. #
  122.  
  123. RESOLUTION_STD = 1
  124. RESOLUTION_FINE = 2
  125. RESOLUTION_300DPI = 3
  126.  
  127. FILE_HEADER_SIZE = 28
  128. PAGE_HEADER_SIZE = 24
  129.  
  130. # **************************************************************************** #
  131.  
  132. ##skip_dn = ["uid=foo,ou=People,dc=example,dc=com",
  133. ##    "uid=bar,ou=People,dc=example,dc=com", "dc=example,dc=com"]
  134.  
  135. class FaxLDIFParser(LDIFParser):
  136.     def __init__(self, input, db):
  137.         LDIFParser.__init__(self, input)
  138.         self.db = db
  139.     
  140.     def handle(self, dn, entry):
  141.         ##for i in skip_dn:
  142.         ##    if i == dn: return
  143.         if dn:
  144.             try:
  145.                 firstname = entry['givenName'][0]
  146.             except KeyError:
  147.                 try:
  148.                     firstname = entry['givenname'][0]
  149.                 except KeyError:
  150.                     firstname = ''
  151.                 
  152.             try:
  153.                 lastname = entry['sn'][0]
  154.             except KeyError:
  155.                 lastname = ''
  156.             
  157.             try:
  158.                 nickname = entry['cn'][0]
  159.             except KeyError:
  160.                 nickname = firstname + ' ' + lastname
  161.             
  162.             try:
  163.                 fax = entry['facsimiletelephonenumber'][0] # fax
  164.             except KeyError:
  165.                 try:
  166.                     fax = entry['fax'][0]
  167.                 except KeyError:
  168.                     fax  = ''
  169.                 
  170.             grps = []
  171.             try:
  172.                 grps = entry['ou']
  173.             except KeyError:
  174.                 pass
  175.                 
  176.             try:
  177.                 title = entry['title'][0]
  178.             except KeyError:
  179.                 title = ''
  180.              
  181.             if nickname:
  182.                 log.debug("%s, %s, %s, %s, %s, %s, %s" % ( nickname, title, firstname, lastname, fax, grps, dn))
  183.                 self.db.set(nickname, title, firstname, lastname, fax, grps, dn)
  184.     
  185.  
  186. # **************************************************************************** #
  187. class FaxAddressBook(object): # Pickle based address book
  188.     def __init__(self):
  189.         self._data = {}
  190.         #
  191.         # { 'name' : {'name': 'name',
  192.         #             'firstname' : u'', 
  193.         #             'lastname': u',
  194.         #             'title' : u'', 
  195.         #             'fax': u'',
  196.         #             'groups' : [u'', u'', ...],
  197.         #             'notes' : u'', } ...
  198.         # }
  199.         #
  200.         self.load()
  201.  
  202.     def load(self):
  203.         self._fab = os.path.join(prop.user_dir, "fab.pickle")
  204.         old_fab = os.path.join(prop.user_dir, "fab.db")
  205.  
  206.         # Load the existing pickle if present
  207.         if os.path.exists(self._fab):
  208.             pickle_file = open(self._fab, "r")
  209.             self._data = cPickle.load(pickle_file)
  210.             pickle_file.close()
  211.  
  212.         else:
  213.             self.save() # save the empty file to create the file
  214.  
  215.  
  216.     def set(self, name, title, firstname, lastname, fax, groups, notes):
  217.         try:
  218.             grps = [unicode(s) for s in groups]
  219.         except UnicodeDecodeError:
  220.             grps = [unicode(s.decode('utf-8')) for s in groups]
  221.  
  222.         self._data[unicode(name)] = {'name' : unicode(name),
  223.                                     'title': unicode(title), 
  224.                                     'firstname': unicode(firstname),
  225.                                     'lastname': unicode(lastname),
  226.                                     'fax': unicode(fax), 
  227.                                     'notes': unicode(notes),
  228.                                     'groups': grps}
  229.  
  230.         self.save()
  231.  
  232.     insert = set
  233.  
  234.     def get(self, name):
  235.         return self._data.get(name, None)
  236.  
  237.     select = get
  238.  
  239.     def get_all_groups(self):
  240.         all_groups = []
  241.         for e, v in self._data.items():
  242.             for g in v['groups']:
  243.                 if g not in all_groups:
  244.                     all_groups.append(g)
  245.         return all_groups
  246.  
  247.     def get_all_records(self):
  248.         return self._data
  249.  
  250.     def get_all_names(self):
  251.         return self._data.keys()
  252.  
  253.     def save(self):
  254.         try:
  255.             pickle_file = open(self._fab, "w")
  256.             cPickle.dump(self._data, pickle_file, cPickle.HIGHEST_PROTOCOL)
  257.             pickle_file.close()
  258.         except IOError:
  259.             log.error("I/O error saving fab file.")
  260.  
  261.     def clear(self):
  262.         self._data = {}
  263.  
  264.     def delete(self, name):
  265.         if name in self._data: #self.current
  266.             del self._data[name]
  267.             return True
  268.  
  269.         return False
  270.  
  271.     def last_modification_time(self):
  272.         try:
  273.             return os.stat(self._fab).st_mtime
  274.         except OSError:
  275.             return 0
  276.  
  277.     def update_groups(self, group, members):
  278.         for e, v in self._data.items():
  279.             if v['name'] in members: # membership indicated
  280.                 if not group in v['groups']:
  281.                     v['groups'].append(unicode(group))
  282.             else:
  283.                 if group in v['groups']:
  284.                     v['groups'].remove(unicode(group))
  285.  
  286.     def delete_group(self, group):
  287.         for e, v in self._data.items():
  288.             if group in v['groups']:
  289.                 v['groups'].remove(unicode(group))
  290.  
  291.     def group_members(self, group):
  292.         members = []
  293.         for e, v in self._data.items():
  294.             if group in v['groups']:
  295.                 members.append(e)
  296.         return members
  297.         
  298.     def import_ldif(self, filename):
  299.         try:
  300.             parser = FaxLDIFParser(open(filename, 'r'), self)
  301.             parser.parse()
  302.             return True, ''
  303.         except ValueError, e:
  304.             return False, e.message
  305.             
  306.     def import_vcard(self, filename):
  307.         for card in vcard.VCards(vcard.VFile(vcard.opentextfile(filename))):
  308.             log.debug(card)
  309.             
  310.             if card['name']:
  311.                 fax = ''
  312.                 for x in range(1,9999):
  313.                     if x == 1:
  314.                         s = 'phone'
  315.                     else:
  316.                         s = 'phone%d' % x
  317.                         
  318.                     try:
  319.                         card[s]
  320.                     except KeyError:
  321.                         break
  322.                     else:
  323.                         if 'fax' in card[s]['type']:
  324.                             fax = card[s]['number']
  325.                             break
  326.                 
  327.                 org = card.get('organisation', '')
  328.                 if org:
  329.                     org = [org]
  330.                 else:
  331.                     org = card.get('categories', '').split(';')
  332.                     if not org:
  333.                         org = []
  334.                 
  335.                 self.set(card['name'], '', card.get('first name', ''), card.get('last name', ''), 
  336.                     fax, org, card.get('notes', ''))
  337.         
  338.         return True, ''
  339.  
  340.  
  341. # **************************************************************************** #
  342. class FaxDevice(device.Device):
  343.  
  344.     def __init__(self, device_uri=None, printer_name=None,
  345.                  callback=None, 
  346.                  fax_type=FAX_TYPE_NONE,
  347.                  disable_dbus=False):
  348.  
  349.         device.Device.__init__(self, device_uri, printer_name,
  350.                                None, callback, disable_dbus)
  351.  
  352.         self.send_fax_thread = None
  353.         self.upload_log_thread = None
  354.         self.fax_type = fax_type
  355.         
  356.         if not disable_dbus:
  357.             session_bus = dbus.SessionBus()
  358.             self.service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
  359.         else:
  360.             self.service = None
  361.  
  362.  
  363.     def setPhoneNum(self, num):
  364.         raise AttributeError
  365.  
  366.     def getPhoneNum(self):
  367.         raise AttributeError
  368.  
  369.     phone_num = property(getPhoneNum, setPhoneNum)
  370.  
  371.  
  372.     def setStationName(self, name):
  373.         raise AttributeError
  374.  
  375.     def getStationName(self):
  376.         raise AttributeError
  377.  
  378.     station_name = property(getStationName, setStationName)
  379.  
  380.     def setDateAndTime(self):
  381.         raise AttributeError
  382.  
  383.     def uploadLog(self):
  384.         raise AttributeError
  385.  
  386.     def isUploadLogActive(self):
  387.         raise AttributeError
  388.  
  389.     def waitForUploadLogThread(self):
  390.         raise AttributeError
  391.  
  392.     def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='', 
  393.                   cover_func=None, preserve_formatting=False, printer_name='', 
  394.                   update_queue=None, event_queue=None):
  395.  
  396.         raise AttributeError
  397.  
  398.     def isSendFaxActive(self):
  399.         if self.send_fax_thread is not None:
  400.             return self.send_fax_thread.isAlive()
  401.         else:
  402.             return False
  403.  
  404.     def waitForSendFaxThread(self):
  405.         if self.send_fax_thread is not None and \
  406.             self.send_fax_thread.isAlive():
  407.  
  408.             try:
  409.                 self.send_fax_thread.join()
  410.             except KeyboardInterrupt:
  411.                 pass
  412.  
  413.  
  414. # **************************************************************************** #
  415.  
  416.  
  417. def getFaxDevice(device_uri=None, printer_name=None,
  418.                  callback=None, 
  419.                  fax_type=FAX_TYPE_NONE,
  420.                  disable_dbus=False):
  421.                  
  422.     if fax_type == FAX_TYPE_NONE:
  423.         if device_uri is None and printer_name is not None:
  424.             printers = cups.getPrinters()
  425.     
  426.             for p in printers:
  427.                 if p.name.lower() == printer_name.lower():
  428.                     device_uri = p.device_uri
  429.                     break
  430.             else:
  431.                 raise Error(ERROR_DEVICE_NOT_FOUND)
  432.                 
  433.         if device_uri is not None:
  434.             mq = device.queryModelByURI(device_uri)
  435.             fax_type = mq['fax-type']
  436.             
  437.     log.debug("fax-type=%d" % fax_type)
  438.                     
  439.     if fax_type in (FAX_TYPE_BLACK_SEND_EARLY_OPEN, FAX_TYPE_BLACK_SEND_LATE_OPEN):
  440.         from pmlfax import PMLFaxDevice
  441.         return PMLFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
  442.     
  443.     elif fax_type == FAX_TYPE_SOAP:
  444.         from soapfax import SOAPFaxDevice
  445.         return SOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
  446.     
  447.     else:
  448.         raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
  449.  
  450. # **************************************************************************** #
  451.  
  452.  
  453. # TODO: Define these in only 1 place!
  454. STATE_DONE = 0
  455. STATE_ABORTED = 10
  456. STATE_SUCCESS = 20
  457. STATE_BUSY = 25
  458. STATE_READ_SENDER_INFO = 30
  459. STATE_PRERENDER = 40
  460. STATE_COUNT_PAGES = 50
  461. STATE_NEXT_RECIPIENT = 60
  462. STATE_COVER_PAGE = 70
  463. STATE_SINGLE_FILE = 80
  464. STATE_MERGE_FILES = 90
  465. STATE_SINGLE_FILE = 100
  466. STATE_SEND_FAX = 110
  467. STATE_CLEANUP = 120
  468. STATE_ERROR = 130 
  469.     
  470. class FaxSendThread(threading.Thread):
  471.     def __init__(self, dev, service, phone_num_list, fax_file_list, 
  472.                  cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
  473.                  printer_name='', update_queue=None, event_queue=None):
  474.  
  475.         threading.Thread.__init__(self)
  476.         
  477.         self.dev = dev # device.Device
  478.         self.service = service # dbus proxy to status server object
  479.         self.phone_num_list = phone_num_list
  480.         self.fax_file_list = fax_file_list
  481.         self.update_queue = update_queue
  482.         self.event_queue = event_queue
  483.         self.cover_message = cover_message
  484.         self.cover_re = cover_re
  485.         self.cover_func = cover_func
  486.         self.current_printer = printer_name
  487.         self.stream = StringIO()  
  488.         self.prev_update = ''
  489.         self.remove_temp_file = False
  490.         self.preserve_formatting = preserve_formatting
  491.         self.results = {} # {'file' : error_code,...}
  492.         self.cover_page_present = False
  493.         self.recipient_file_list = []
  494.         self.f = None # final file of fax data to send (pages merged)
  495.         self.job_hort_dpi = 0
  496.         self.job_hort_dpi = 0
  497.         self.job_vert_dpi = 0
  498.         self.job_page_size = 0
  499.         self.job_resolution = 0
  500.         self.job_encoding = 0
  501.  
  502.  
  503.     def pre_render(self, state):
  504.         # pre-render each page that needs rendering
  505.         # except for the cover page
  506.         self.cover_page_present = False
  507.         log.debug(self.fax_file_list)
  508.  
  509.         for fax_file in self.fax_file_list: # (file, type, desc, title)
  510.             fax_file_name, fax_file_type, fax_file_desc, \
  511.                 fax_file_title, fax_file_pages = fax_file
  512.  
  513.             if fax_file_type == "application/hplip-fax-coverpage": # render later
  514.                 self.cover_page_present = True
  515.                 log.debug("Skipping coverpage")
  516.  
  517.             #if fax_file_type == "application/hplip-fax": # already rendered
  518.             else:
  519.                 self.rendered_file_list.append((fax_file_name, "application/hplip-fax",
  520.                     "HP Fax", fax_file_title))
  521.  
  522.                 log.debug("Processing pre-rendered file: %s (%d pages)" % 
  523.                     (fax_file_name, fax_file_pages))
  524.  
  525.             if self.check_for_cancel():
  526.                 state = STATE_ABORTED
  527.  
  528.         log.debug(self.rendered_file_list)  
  529.  
  530.         if self.check_for_cancel():
  531.             state = STATE_ABORTED
  532.  
  533.         return state
  534.  
  535.  
  536.     def count_pages(self, state):
  537.         self.recipient_file_list = self.rendered_file_list[:]
  538.         log.debug("Counting total pages...")
  539.         self.job_total_pages = 0
  540.         log.debug(self.recipient_file_list)
  541.  
  542.         i = 0
  543.         for fax_file in self.recipient_file_list: # (file, type, desc, title)
  544.             fax_file_name = fax_file[0]
  545.             log.debug("Processing file (counting pages): %s..." % fax_file_name)
  546.  
  547.             #self.write_queue((STATUS_PROCESSING_FILES, self.job_total_pages, ''))
  548.  
  549.             if os.path.exists(fax_file_name):
  550.                 self.results[fax_file_name] = ERROR_SUCCESS
  551.                 fax_file_fd = file(fax_file_name, 'r')
  552.                 header = fax_file_fd.read(FILE_HEADER_SIZE)
  553.  
  554.                 magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  555.                     resolution, encoding, reserved1, reserved2 = \
  556.                         self.decode_fax_header(header)
  557.  
  558.                 if magic != 'hplip_g3':
  559.                     log.error("Invalid file header. Bad magic.")
  560.                     self.results[fax_file_name] = ERROR_FAX_INVALID_FAX_FILE
  561.                     state = STATE_ERROR
  562.                     continue
  563.  
  564.                 if not i:
  565.                     self.job_hort_dpi, self.job_vert_dpi, self.job_page_size, \
  566.                         self.job_resolution, self.job_encoding = \
  567.                         hort_dpi, vert_dpi, page_size, resolution, encoding
  568.  
  569.                     i += 1
  570.                 else:
  571.                     if self.job_hort_dpi != hort_dpi or \
  572.                         self.job_vert_dpi != vert_dpi or \
  573.                         self.job_page_size != page_size or \
  574.                         self.job_resolution != resolution or \
  575.                         self.job_encoding != encoding:
  576.  
  577.                         log.error("Incompatible options for file: %s" % fax_file_name)
  578.                         self.results[fax_file_name] = ERROR_FAX_INCOMPATIBLE_OPTIONS
  579.                         state = STATE_ERROR
  580.  
  581.  
  582.                 log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  583.                           (magic, version, total_pages, hort_dpi, 
  584.                            vert_dpi, page_size, resolution, encoding))
  585.  
  586.                 self.job_total_pages += total_pages
  587.  
  588.                 fax_file_fd.close()
  589.  
  590.             else:
  591.                 log.error("Unable to find HP Fax file: %s" % fax_file_name)
  592.                 self.results[fax_file_name] = ERROR_FAX_FILE_NOT_FOUND
  593.                 state = STATE_ERROR
  594.                 break
  595.  
  596.             if self.check_for_cancel():
  597.                 state = STATE_ABORTED
  598.                 break
  599.  
  600.  
  601.         if self.cover_page_present:
  602.             self.job_total_pages += 1 # Cover pages are truncated to 1 page
  603.  
  604.         log.debug("Total fax pages=%d" % self.job_total_pages)
  605.  
  606.         return state
  607.         
  608.     def decode_fax_header(self, header):
  609.         try:
  610.             return struct.unpack(">8sBIHHBBBII", header)
  611.         except struct.error:
  612.             return -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
  613.  
  614.     def decode_page_header(self, header):
  615.         try:
  616.             return struct.unpack(">IIIIII", header)
  617.         except struct.error:
  618.             return -1, -1, -1, -1, -1, -1
  619.  
  620.     def cover_page(self,  recipient):
  621.         if self.job_total_pages > 1:
  622.             state = STATE_MERGE_FILES
  623.         else:
  624.             state = STATE_SINGLE_FILE
  625.  
  626.         if self.cover_page_present:
  627.             log.debug("Creating cover page for recipient: %s" % recipient['name'])
  628.             fax_file, canceled = self.render_cover_page(recipient)
  629.  
  630.             if canceled:
  631.                 state = STATE_ABORTED
  632.             elif not fax_file:
  633.                 state = STATE_ERROR # timeout
  634.             else:
  635.                 self.recipient_file_list.insert(0, (fax_file, "application/hplip-fax", 
  636.                                                     "HP Fax", 'Cover Page'))
  637.  
  638.                 log.debug("Cover page G3 file: %s" % fax_file)
  639.  
  640.                 self.results[fax_file] = ERROR_SUCCESS
  641.  
  642.         return state
  643.  
  644.     def single_file(self, state):
  645.         state = STATE_SEND_FAX
  646.  
  647.         log.debug("Processing single file...")
  648.         self.f = self.recipient_file_list[0][0]
  649.  
  650.         try:
  651.             f_fd = file(self.f, 'r')
  652.         except IOError:
  653.             log.error("Unable to open fax file: %s" % self.f)
  654.             state = STATE_ERROR
  655.         else:
  656.             header = f_fd.read(FILE_HEADER_SIZE)
  657.  
  658.             magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  659.                 resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
  660.  
  661.             self.results[self.f] = ERROR_SUCCESS
  662.  
  663.             if magic != 'hplip_g3':
  664.                 log.error("Invalid file header. Bad magic.")
  665.                 self.results[self.f] = ERROR_FAX_INVALID_FAX_FILE
  666.                 state = STATE_ERROR
  667.  
  668.             log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  669.                       (magic, version, total_pages, hort_dpi, vert_dpi, 
  670.                        page_size, resolution, encoding))
  671.  
  672.             f_fd.close()
  673.  
  674.         return state
  675.  
  676.  
  677.     def merge_files(self, state):
  678.         log.debug("%s State: Merge multiple files" % ("*"*20))
  679.         log.debug(self.recipient_file_list)
  680.         log.debug("Merging g3 files...")
  681.         self.remove_temp_file = True
  682.  
  683.         if self.job_total_pages:
  684.             f_fd, self.f = utils.make_temp_file()
  685.             log.debug("Temp file=%s" % self.f)
  686.  
  687.             data = struct.pack(">8sBIHHBBBII", "hplip_g3", 1L, self.job_total_pages,  
  688.                 self.job_hort_dpi, self.job_vert_dpi, self.job_page_size, 
  689.                 self.job_resolution, self.job_encoding, 
  690.                 0L, 0L)
  691.  
  692.             os.write(f_fd, data)
  693.  
  694.             job_page_num = 1
  695.  
  696.             for fax_file in self.recipient_file_list:
  697.                 fax_file_name = fax_file[0]
  698.                 log.debug("Processing file: %s..." % fax_file_name)
  699.  
  700.                 if self.results[fax_file_name] == ERROR_SUCCESS:
  701.                     fax_file_fd = file(fax_file_name, 'r')
  702.                     header = fax_file_fd.read(FILE_HEADER_SIZE)
  703.  
  704.                     magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  705.                         resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
  706.  
  707.                     if magic != 'hplip_g3':
  708.                         log.error("Invalid file header. Bad magic.")
  709.                         state = STATE_ERROR
  710.                         break
  711.  
  712.                     log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  713.                               (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  714.  
  715.                     for p in range(total_pages):
  716.                         header = fax_file_fd.read(PAGE_HEADER_SIZE)
  717.  
  718.                         page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
  719.                             self.decode_page_header(header)
  720.  
  721.                         if page_num == -1:
  722.                             log.error("Page header error")
  723.                             state - STATE_ERROR
  724.                             break
  725.  
  726.                         header = struct.pack(">IIIIII", job_page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, 0L)
  727.                         os.write(f_fd, header)
  728.  
  729.                         self.write_queue((STATUS_PROCESSING_FILES, job_page_num, ''))
  730.  
  731.                         log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%s" %
  732.                                   (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))                    
  733.  
  734.                         os.write(f_fd, fax_file_fd.read(bytes_to_read))
  735.                         job_page_num += 1
  736.  
  737.                     fax_file_fd.close()
  738.  
  739.                     if self.check_for_cancel():
  740.                         state = STATE_ABORTED
  741.                         break
  742.  
  743.                 else:
  744.                     log.error("Skipping file: %s" % fax_file_name)
  745.                     continue
  746.  
  747.             os.close(f_fd)
  748.             log.debug("Total pages=%d" % self.job_total_pages)
  749.  
  750.         return state
  751.  
  752.  
  753.     def next_recipient_gen(self):
  754.         for a in self.phone_num_list:
  755.             yield a
  756.  
  757.  
  758.     def render_file(self, path, title, mime_type, force_single_page=False):
  759.         all_pages = True 
  760.         page_range = ''
  761.         page_set = 0
  762.         nup = 1
  763.  
  764.         cups.resetOptions()
  765.  
  766.         if mime_type in ["application/x-cshell",
  767.                          "application/x-perl",
  768.                          "application/x-python",
  769.                          "application/x-shell",
  770.                          "text/plain",]:
  771.  
  772.             cups.addOption('prettyprint')
  773.  
  774.         if nup > 1:
  775.             cups.addOption('number-up=%d' % nup)
  776.  
  777.         if force_single_page:
  778.             cups.addOption('page-ranges=1') # Force coverpage to 1 page
  779.  
  780.         sent_job_id = cups.printFile(self.current_printer, path, title)
  781.         cups.resetOptions()
  782.  
  783.         log.debug("Job ID=%d" % sent_job_id)    
  784.         job_id = 0
  785.  
  786.         time.sleep(1)
  787.  
  788.         fax_file = ''
  789.         complete = False
  790.  
  791.         end_time = time.time() + 300.0 # wait for 5 min. max 
  792.         while time.time() < end_time:
  793.             log.debug("Waiting for fax...")
  794.             
  795.             result = list(self.service.CheckForWaitingFax(self.dev.device_uri, prop.username, sent_job_id))
  796.  
  797.             fax_file = str(result[7])
  798.             log.debug("Fax file=%s" % fax_file)
  799.             
  800.             if fax_file:
  801.                 break
  802.             
  803.             if self.check_for_cancel():
  804.                 log.error("Render canceled. Canceling job #%d..." % sent_job_id)
  805.                 cups.cancelJob(sent_job_id)
  806.                 return '', True
  807.  
  808.             time.sleep(1)
  809.  
  810.         else:
  811.             log.error("Timeout waiting for rendering. Canceling job #%d..." % sent_job_id)
  812.             cups.cancelJob(sent_job_id)
  813.             return '', False
  814.  
  815.         return fax_file, False
  816.  
  817.  
  818.     def check_for_cancel(self):
  819.         canceled = False
  820.         while self.event_queue.qsize():
  821.             try:
  822.                 event = self.event_queue.get(0)
  823.                 if event[0] == EVENT_FAX_SEND_CANCELED:
  824.                     canceled = True
  825.                     log.debug("Cancel pressed!")
  826.             except Queue.Empty:
  827.                 break
  828.  
  829.         return canceled
  830.  
  831.     def render_cover_page(self, a):
  832.         log.debug("Creating cover page...")
  833.  
  834.         pdf = self.cover_func(page_size=coverpages.PAGE_SIZE_LETTER,
  835.                               total_pages=self.job_total_pages, 
  836.  
  837.                               recipient_name=a['name'], 
  838.                               recipient_phone='', # ???
  839.                               recipient_fax=a['fax'], 
  840.  
  841.                               sender_name=self.sender_name, 
  842.                               sender_phone=user_cfg.fax.voice_phone, 
  843.                               sender_fax=self.sender_fax,
  844.                               sender_email=user_cfg.fax.email_address, 
  845.  
  846.                               regarding=self.cover_re, 
  847.                               message=self.cover_message,
  848.                               preserve_formatting=self.preserve_formatting)
  849.  
  850.         log.debug("PDF File=%s" % pdf)
  851.         fax_file, canceled = self.render_file(pdf, 'Cover Page', "application/pdf", 
  852.             force_single_page=True) 
  853.  
  854.         try:
  855.             os.remove(pdf)
  856.         except IOError:
  857.             pass
  858.  
  859.         return fax_file, canceled
  860.  
  861.  
  862.     def write_queue(self, message):
  863.         if self.update_queue is not None and message != self.prev_update:
  864.             self.update_queue.put(message)
  865.             time.sleep(0)
  866.             self.prev_update = message
  867.             
  868.     
  869.     def run(self):
  870.         pass
  871.         
  872.         
  873.  
  874.